Creating and Releasing Objects
This section discusses how your part editor allocates and releases OpenDoc objects, and how it should release unneeded memory when requested to do so by OpenDoc.Factory Methods
Any OpenDoc object that you create must be instantiated by a factory method, a method of one class used to create an instance of another class. You should never use the C++new
operator, for example, to create an object of an OpenDoc class (except for subclasses, such asODExtension
, through which you extend OpenDoc and for which your part is the factory).Table 11-1 lists the factory methods you should use to instantiate any of the OpenDoc classes whose objects your part editor might ever need to create. The table lists methods that create new objects as well as those that return previously created objects, whether currently in memory or stored persistently. Note that your own part editor has the factory methods for the classes
ODEmbeddedFramesIterator
and any of its own extension objects, includingODSemanticInterface
. Note also thatODDraft
has the factory method forODPart
and any of its subclasses, including your part.Reference-Counted Objects
The use of reference-counted objects is part of the OpenDoc memory-
management scheme. Reference-counted objects maintain a count of the current number of references to them; that is, they are shared objects that are aware of how many other objects are making use of them at any one time.During an OpenDoc session, many objects are created. Because the relationship among objects can be very complex, it may be difficult for a part or for OpenDoc to determine when it is safe to delete an object from memory. Reference counting is a way to determine when runtime objects can be deleted and valuable memory space reclaimed.
A reference count is 0 or a positive integer. A value greater than 0 means that at least one reference to the object currently exists, and thus the object must not be removed from memory. All descendants of
ODRefCntObject
(including frames, links, link sources, and parts) are reference-counted.Each reference-counted object is created through a call to its factory method, the method (usually in another class) responsible for creating that object and initializing its reference count. For example, a draft object is created by calling the
CreateDraft
orAcquireDraft
method of a document object. Likewise, a frame object is created by calling theCreateFrame
orAcquireFrame
method of a draft object. See Table 11-1 for a list of factory methods.These are the reference-counted OpenDoc objects:
ODContainer ODLinkSource ODSettingsExtension ODDocument ODMenuBar ODShape ODDraft ODPart ODStorageUnit ODExtension ODPersistentObject ODTransform ODFrame ODRefCntObject ODWindow ODLink ODSemanticInterface When it is first created by its factory object, a reference-counted object has a reference count of 1. Thus, the frame object returned by a draft's
CreateFrame
method always has a reference count of 1, because that method always creates a new object. The frame object returned by a draft'sAcquireFrame
method, however, may have a reference count greater than 1, because that method may return a new reference to a pre-existing frame.Calling the
Release
method of a reference-counted object decrements its reference count; calling itsAcquire
method increments its reference count. Calling theRelease
method of a reference-counted object when its reference count is 1 causes its new reference count to be set to 0 and may result in the object being deleted from memory. It is an error to attempt to access an object whose reference count is 0.Each reference-counted object stores its own reference count and returns it to callers of its
GetRefCount
method. When the object's reference count goes to 0, the object is responsible for notifying its factory object so that the factory object can delete it from memory. The factory object can choose to delete it immediately, or, for efficiency, keep it in memory until OpenDoc calls itsPurge
method when memory is needed.Whenever your part editor writes a reference to a reference-counted object into a data structure, it should increment that object's reference count by calling its
Acquire
method. When your part editor is finished working with that object, it should call the object'sRelease
method.Because shape and transform objects are reference-counted and are widely used, it is important to remember always to release them instead of deleting them. Any method call with which you acquire a shape or transform must be balanced by a subsequent call to
Release
.
clipShape = facet->AcquireClipShape(ev, biasCanvas); ... clipShape->Release(ev); clipShape = kODNULL;Likewise, any method call that assigns a reference-counted object to another object increases its reference count. Therefore, you can immediately release your own reference to it.
newShape = facet->CreateShape(ev); ... facet->ChangeGeometry(ev, newShape, transform, biasCanvas); newShape->Release(ev); newShape = kODNULL;As a reference-counted object, your part is responsible for implementing an override of the
- Acquire versus Get
- Methods whose names begin with Acquire increment the reference counts of the objects they return; methods whose names begin with Get do not. Therefore, every call to
ODDraft::AcquireFrame
, for example, must be balanced with a call toODFrame::Release
. A call toFacet::GetCanvas
, on the other hand, requires no corresponding call to decrement reference count.![]()
Release
method and for calling theReleasePart
method of its draft object when your part's reference count goes to 0. Your part does not need to release all of its references or do any other shutting down or deallocation at that point. TheReleasePart
method calls your part's destructor, which takes care of deallocation. However, your part could get rid of unneeded structures or services before callingReleasePart
. For example, a communications part editor may choose to close its driver as soon as its reference count reaches 0.If your part has a reference count of 0, but calling your draft's
ReleasePart
method has not resulted in your part's destruction (perhaps because no purge has been performed), the draft object can retrieve and reuse your part. In that case, any structures or services you deallocated when the reference count went to 0 must be reallocated. YourIncrementRefCount
method can perform those tasks for the case in which the reference count goes from 0 to 1.Note that your part is destroyed after its
ReleaseAll
method is called, regardless of its current reference count, when its draft closes. See "Closing Your Part"
- Testing objects for equality
- If you need to compare two existing OpenDoc objects for equality, don't simply compare their pointers. Instead, call the
IsEqualTo
method (defined inODObject
) of either object.IsEqualTo
always gives the correct result, even in a distributed environment when comparing pointers may fail. However, be sure never to call theIsEqualTo
method of a null object reference.![]()
Handling Byte Arrays and Other Parameters
Many parameters to OpenDoc methods consist of references to objects or to other data that needs special attention in terms of allocating and releasing the storage associated with it. This section discusses parameter handling for byte arrays, strings, and objects.To make the OpenDoc programming interface CORBA-compliant and capable of distributed execution, OpenDoc requires you to use a certain format when passing method parameters that point to buffers containing variable-length data.
OpenDoc defines the
ODByteArray
structure as a sequence of octets (unsigned 8-bit values). It consists of a buffer size field, a data length field, and a pointer to the buffer associated with the structure. All variable-length data is passed to or from OpenDoc with byte arrays. For example, the storage-unit methodsGetValue
andSetValue
use byte arrays for the data being passed.The caller of a method that takes variable-length data must place the data in a buffer pointed to by a byte array; the receiver of the data then retrieves the data from the buffer and uses it. Both sender and receiver must understand the underlying type of the data. For example, if your part calls
SetValue
, your part passes a structure of typeODByteArray
, but the method understands the implied type of the contents of the buffer (a storage-unit value).If you are the caller of a method that uses a byte array as a parameter, follow these rules for allocating and releasing the array:
All data passed by means of a byte array must be self-contained; for example, it can't contain pointers to data outside of itself.
- You as caller are responsible for allocating and releasing the storage of the byte array structure itself. You can allocate the byte array either on the stack or in the heap.
- If the byte array is an
in
parameter, you as caller allocate the storage for the data buffer to which the byte array points, and you also release that storage after the method returns and you no longer need the data. (If the method needs to retain the data in the data buffer, it makes a copy.)- If the byte array is an
out
parameter or a function result, the method allocates the storage for the data buffer to which the byte array points; you as caller release that storage when you no longer need it.
If you are the caller of a method that uses a parameter that is a string (type
ODISOString
or one of its equivalents, such asODValueType
) or an OpenDoc object, similar rules apply:
- If the string or object is an
in
parameter, you are responsible for both allocating and releasing its storage.- If the string or object is an
out
parameter or a function result, the method allocates the storage, and you must release the string or object when you no longer need it.
Purging
The OpenDoc document shell manages OpenDoc memory usage within the process in which it executes. When the document shell needs more memory, it can ask other objects in its process to voluntarily surrender noncritical memory that they have been using.Every subclass of
ODObject
, including your part, must implement an override of thePurge
method. When the document shell calls your part'sPurge
method, your part should free any caches or other noncritical buffers or objects, freeing up to the amount of memory requested in thePurge
call. If necessary, your part can first write any data it desires to persistent storage.Purging is the appropriate way to get rid of frames that you have created through lazy internalization (next).
Lazy Internalization
Your part does not necessarily have to maintain frame objects in memory at all times for all of its embedded frames and display frames. You may have many display frames or embedded frames, only a few of which are visible at any one time. In such a case, you might want to reduce memory use by creating frame objects only for those frames that are currently visible. Then, as the user scrolls through your part, resizes your display frame, or brings other display frames of your part into view, you can create frame objects for frames that appear and release frame objects of frames that disappear. This process, which could be called lazy internalization, works like this:
- At initialization, your part can create frame objects for just the visible frames of its embedded parts, as described in the section "Storing and Retrieving Embedded Frames".
- As each additional frame becomes visible through scrolling, resizing, or removal of obscuring content, you create the frame object by calling your draft's
AcquireFrame
method. OpenDoc then calls theDisplayFrameConnected
method of the part (either your part or the embedded part) displayed in the frame.- As each previously visible embedded frame becomes invisible, you can simply leave it in memory but marked as purgeable, or you can call the frame's
Release
method.- Maintain connections to your released frames by saving their storage-unit IDs. If a previously released frame becomes visible again, you can create it once again by calling
AcquireFrame
. OpenDoc in turn calls the part'sDisplayFrameConnected
method once again, if necessary.
Main | Page One | What's New | Apple Computer, Inc. | Find It | Contact Us | Help